/*
 *文件作用: 测试驱动
 */

#include "leptjson.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>  // strtod():string to double
#include <string.h>

static int test_count = 0;  //总测试次数
static int test_pass = 0;   //测试通过次数
static int main_ret = 0;    //测试程序返回值，0 for success

//================================Start:  宏====================================
//基本比较宏定义，用以比较预期结果和实际结果是否一致
//参数:
//  - equality: 预期值和实际值判断相等的方式
//  - expect: 预期值
//  - actual: 实际值
//  - format: 发生错误时, expect 和 actual 的输出格式
#define EXPECT_EQ_BASE(equality, expect, actual, format)                                                               \
    do {                                                                                                               \
        test_count++;                                                                                                  \
        if (equality) {                                                                                                \
            test_pass++;                                                                                               \
        } else {                                                                                                       \
            fprintf(stderr, "%s:%d: expect: " format " actual: " format "\n", __FILE__, __LINE__, expect, actual);     \
            main_ret = 1;                                                                                              \
        }                                                                                                              \
    } while (0)

//比较预期结果和实际结果，指定以int作为格式化方式，基于基本比较宏。结果不匹配时打印错误位置和数值信息。
//参数:
//  - expect: 预期结果
//  - actual: 实际结果
#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d")

//同 EXPECT_EQ_INT, 但以 double 打印 expect 和 actual。
#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%e")

//同上, 以 string 打印 expect 和 actual
#define EXPECT_EQ_STRING(expect, actual, alength)                                                                      \
    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, "%s")

//比较 size_t类型 的数组长度是否一致
#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE(((expect) == (actual)), (size_t)expect, (size_t)actual, "%zu")

//错误码测试宏，要求出现错误时能给出预期错误码，并将节点类型强制置为 LETP_NULL
//参数:
//  - error: 预期错误码
//  - json: json字符串
#define TEST_ERROR(error, json)                                                                                        \
    do {                                                                                                               \
        lept_value v;                                                                                                  \
        v.type = LEPT_BOOLEAN;                                                                                         \
        EXPECT_EQ_INT(error, lept_parse(&v, json));                                                                    \
        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));                                                                   \
        lept_free(&v);                                                                                                 \
    } while (0)

//针对 true, false, null 的value解析测试宏，以 LEPT_NULL 为原始类型
//初始化节点类型，要求解析成功，并正确修改节点类型 参数:
//  - expect_type: 预期结果类型
//  - json: json字符串
#define TEST_LITERAL(expect_type, json)                                                                                \
    do {                                                                                                               \
        lept_value v;                                                                                                  \
        v.type = LEPT_NULL;                                                                                            \
        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));                                                            \
        EXPECT_EQ_INT(expect_type, lept_get_type(&v));                                                                 \
        if (ISBOOLEANTYPE(&v)) {                                                                                       \
            if (ISTRUE(json)) {                                                                                        \
                EXPECT_EQ_INT(1, lept_get_boolean(&v));                                                                \
            }                                                                                                          \
        }                                                                                                              \
    } while (0)

// number 测试宏，检查解析是否成功,以及解析结果类型和解析结果数值
//参数:
//  - expect: 预期结果数字的值
//  - json: json字符串
#define TEST_NUMBER(expect, json)                                                                                      \
    do {                                                                                                               \
        lept_value v;                                                                                                  \
        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));                                                            \
        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));                                                                 \
        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));                                                                 \
    } while (0)

// string 测试宏
#define TEST_STRING(expect_string, json)                                                                               \
    do {                                                                                                               \
        lept_value v;                                                                                                  \
        lept_init(&v);                                                                                                 \
        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));                                                            \
        /*printString(&v);*/                                                                                           \
        EXPECT_EQ_INT(LEPT_STRING, v.type);                                                                            \
        EXPECT_EQ_STRING(expect_string, v.union_struct.str.string, lept_get_string_length(&v));                        \
        lept_free(&v);                                                                                                 \
    } while (0)

// array 测试宏
#define TEST_ARRAY(expect_size, json)                                                                                  \
    do {                                                                                                               \
        lept_value v;                                                                                                  \
        lept_init(&v);                                                                                                 \
        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));                                                            \
        EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));                                                                  \
        EXPECT_EQ_SIZE_T(expect_size, lept_get_array_size(&v));                                                        \
        /*printf("\nsize == %zu\n", v.union_struct.arr.size);*/                                                        \
        /*printArray(&v);*/                                                                                            \
        lept_free(&v);                                                                                                 \
    } while (0)

// object 测试宏
#define TEST_OBJECT(expect_size, json)                                                                                 \
    do {                                                                                                               \
        lept_value v;                                                                                                  \
        lept_init(&v);                                                                                                 \
        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));                                                            \
        EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));                                                                 \
        EXPECT_EQ_SIZE_T(expect_size, lept_get_object_size(&v));                                                       \
        /*printObject(&v);*/                                                                                           \
    } while (0)

// 环回测试: 将字符串解析为树形节点结构，然后再将树形结构重新字符串化, 比较前后结果是否正确 (要求原字符串不包含空白符)
#define ROUNDTRIP(json)                                                                                                \
    do {                                                                                                               \
        lept_value v;                                                                                                  \
        char* json2;                                                                                                   \
        size_t length;                                                                                                 \
        lept_init(&v);                                                                                                 \
        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));                                                            \
        /*printString(&v);*/                                                                                           \
        /*const __uint8_t* p = v.union_struct.str.string;                                                              \
        printf("Now we get the string:");                                                                              \
        while (*p) {                                                                                                   \
            printf("%x", *p++);                                                                                        \
        }                                                                                                              \
        printf("\n");*/                                                                                                \
        EXPECT_EQ_INT(LEPT_STRINGIFY_OK, lept_stringify(&v, &json2, &length));                                         \
        EXPECT_EQ_STRING(json, json2, length);                                                                         \
        lept_free(&v);                                                                                                 \
        /*const __uint8_t* p2 = json2;                                                                                 \
        while (*p2) {                                                                                                  \
            printf("%x ", *p2++);                                                                                      \
        };                                                                                                             \
        NEWLINE;*/                                                                                                     \
        free(json2);                                                                                                   \
    } while (0)

// 换行
#ifndef NEWLINE
#    define NEWLINE printf("\n")
#endif
//================================End:  宏====================================

static void printString(lept_value* v) {
    assert(v != NULL && v->type == LEPT_STRING);
    const char* p = v->union_struct.str.string;
    while (*p) {
        printf("%x ", (__uint8_t)*p++);
    }
    printf("\n");
}

static void printObject(lept_value* v);  // 预先声明，以在 printArray 中调用

// 打印 数组内容
static void printArray(lept_value* v) {
    assert(v != NULL && v->type == LEPT_ARRAY);
    size_t i;
    for (i = 0; i < v->union_struct.arr.size; ++i) {
        lept_value* tmpV = lept_get_array_element(v, i);
        printf("#%zu ==> value_type: ", i);
        switch (lept_get_type(tmpV)) {
            case LEPT_NULL: printf("null\n"); break;
            case LEPT_BOOLEAN: printf("boolean, value: %s\n", (lept_get_boolean(tmpV) == 1 ? "true" : "false")); break;
            case LEPT_NUMBER: printf("number, value: %lf\n", lept_get_number(tmpV)); break;
            case LEPT_STRING: printf("string, value: %s\n", lept_get_string(tmpV)); break;
            case LEPT_ARRAY:
                printf("array, size: %zu\n", lept_get_array_size(tmpV));
                printArray(tmpV);
                break;
            case LEPT_OBJECT: printObject(tmpV); break;
            default: break;
        }
    }
}

// 打印 对象成员
static void printObject(lept_value* v) {
    assert(v != NULL && v->type == LEPT_OBJECT);
    size_t i;
    printf("\nsize == %zu\n", lept_get_object_size(v));
    for (i = 0; i < v->union_struct.obj.size; ++i) {
        lept_member* tmpMem = &v->union_struct.obj.lept_members[i];
        lept_value* tmpV = &tmpMem->value;
        printf("#%zu ==> key: %s, key_value_type: ", i, tmpMem->key);
        switch (lept_get_type(tmpV)) {
            case LEPT_NULL: printf("null\n"); break;
            case LEPT_BOOLEAN: printf("boolean, value: %s\n", (lept_get_boolean(tmpV) == 1 ? "true" : "false")); break;
            case LEPT_NUMBER: printf("number, value: %lf\n", lept_get_number(tmpV)); break;
            case LEPT_STRING: printf("string, value: %s\n", lept_get_string(tmpV)); break;
            case LEPT_ARRAY:
                printf("array, size: %zu\n", lept_get_array_size(tmpV));
                printArray(tmpV);
                break;
            case LEPT_OBJECT:
                printf("object, size: %zu\n", lept_get_object_size(tmpV));
                printObject(tmpV);
                break;
            default: break;
        }
    }
}

//=============================Start: 字面量====================================
// true, false, null 字面量解析测试集合
static void test_parse_expect_literal() {
    TEST_LITERAL(LEPT_BOOLEAN, "true");   //测试true值解析
    TEST_LITERAL(LEPT_BOOLEAN, "false");  //测试false值解析
    TEST_LITERAL(LEPT_NULL, "null");      //测试null值解析
}

//非法字面量解析测试集合
static void test_parse_invalid_literal() {
    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "false f");  //测试多值输入
    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, " \t\n\r");       //测试无值输入
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "monday");       //测试非法输入
}

//字面量解析集合
static void test_parse_literal() {
    test_parse_expect_literal();   //测试合法字面量
    test_parse_invalid_literal();  //测试非法字面量
}
//===========================End: 字面量===============================

//===========================Start:  数字==============================
// number 解析测试集合
static void test_parse_expect_number() {
    TEST_NUMBER(0.0, "0");
    TEST_NUMBER(0.0, "-0");
    TEST_NUMBER(0.0, "-0.0");
    TEST_NUMBER(1.0, "1");
    TEST_NUMBER(-1.0, "-1");
    TEST_NUMBER(1.5, "1.5");
    TEST_NUMBER(-1.5, "-1.5");
    TEST_NUMBER(3.1416, "3.1416");
    TEST_NUMBER(1E10, "1E10");
    TEST_NUMBER(1e10, "1e10");
    TEST_NUMBER(1E+10, "1E+10");
    TEST_NUMBER(1E-10, "1E-10");
    TEST_NUMBER(-1E10, "-1E10");
    TEST_NUMBER(-1e10, "-1e10");
    TEST_NUMBER(-1E+10, "-1E+10");
    TEST_NUMBER(-1E-10, "-1E-10");
    TEST_NUMBER(1.234E+10, "1.234E+10");
    TEST_NUMBER(1.234E-10, "1.234E-10");
    TEST_NUMBER(0.0, "1e-10000"); /* must underflow */

    TEST_NUMBER(1.0000000000000002, "1.0000000000000002");            // the smallest number > 1
    TEST_NUMBER(4.9406564584124654e-324, "4.9406564584124654e-324");  // Min. subnormal positive double
    TEST_NUMBER(-4.9406564584124654e-324, "-4.9406564584124654e-324");
    TEST_NUMBER(2.2250738585072009e-308, "2.2250738585072009e-308");    // Max. subnormal double
    TEST_NUMBER(-2.2250738585072009e-308, "-2.2250738585072009e-308");  //
    TEST_NUMBER(2.2250738585072014e-308, "2.2250738585072014e-308");    // Min. normal positive double
    TEST_NUMBER(-2.2250738585072014e-308, "-2.2250738585072014e-308");
    TEST_NUMBER(1.7976931348623157e308, "1.7976931348623157e308");  // Max. Double
    TEST_NUMBER(-1.7976931348623157e308, "-1.7976931348623157e308");
}

//测试非法数字
static void test_parse_invalid_number() {
    /* invalid number */
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+0");
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+1");
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, ".123");  // at least one digit before '.'
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "1.");    // at least one digit after '.'
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "INF");
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "inf");
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "NAN");
    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nan");
}

static void test_parse_number() {
    test_parse_expect_number();   //测试合法数字
    test_parse_invalid_number();  //测试非法数字
}
//===========================End:  数字==============================

//===========================Start: 字符串==============================
static void test_access_string() {
    lept_value v;
    lept_init(&v);
    lept_set_string(&v, "true", 4);
    EXPECT_EQ_STRING("true", lept_get_string(&v), 4);
}

static void test_parse_expect_string() {
    TEST_STRING("normal characters", "\"normal characters\"");    // normal characters
    TEST_STRING("quotation mark\"", "\"quotation mark\\\"\"");    // quotation mark
    TEST_STRING("reverse solidus\\", "\"reverse solidus\\\\\"");  // reverse solidus
    TEST_STRING("solidus\/", "\"solidus\\\/\"");                  // solidus
    TEST_STRING("backspace\b", "\"backspace\\b\"");               // backspace
    TEST_STRING("form feed\f", "\"form feed\\f\"");               // form feed
    TEST_STRING("line feed\n", "\"line feed\\n\"");               // line feed
    TEST_STRING("carriage return\r", "\"carriage return\\r\"");   // carriage return
    TEST_STRING("tab\t", "\"tab\\t\"");                           // tab
}
static void test_parse_invalid_string() {
    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"invalid_escape\\p\"");  // invalid escape \p
    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, "\"miss_quotation_mark");    // miss quotation mark
}

//测试预期的 utf-8 字符串解析结果
test_parse_expect_utf8_string() {
    // char t1[2] = {0x60, 0};
    // TEST_STRING(t1, "\"\\u0060\"");

    // char t2[3] = {0xd4, 0xac, 0};
    // TEST_STRING(t2, "\"\\u052c\"");

    // char t3[4] = {0xea, 0xa5, 0xb3, 0};
    // TEST_STRING(t3, "\"\\ua973\"");

    char t4[5] = {0xf2, 0x9c, 0x8a, 0xac, 0};
    TEST_STRING(t4, "\"\\uda30deac\"");

    // char t5[5] = {0xf4, 0x8f, 0xbf, 0xbf, 0};
    // TEST_STRING(t5, "\"\\udbffdfff\"");
}

//测试非法的 utf-8 字符串解析错误码
static void test_invalid_utf8_string() {
    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\udbff\"");
    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\udbffe000\"");
}
//===========================End: 字符串==============================

//===========================Start: 数组==============================
// 测试数组解析
static void test_parse_expect_array() {
    TEST_ARRAY(0, "[ ]");
    TEST_ARRAY(5, "[null, false, true, 123, \"abc\"]");
    TEST_ARRAY(6, "[null, true, false, 123, \"abc\", [ [[0], 1], 2, 3] ]");
}

static void test_parse_invalid_array() {
    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1 2]");
    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1, 2");
}
//===========================End: 数组==============================

//===========================Start: 数组==============================
static void test_parse_expect_object() {
    TEST_OBJECT(1, "{\"null_member\":null}");
    TEST_OBJECT(1, "{\"boolean_member_true\":true}");
    TEST_OBJECT(1, "{\"boolean_member_false\":false}");
    TEST_OBJECT(1, "{\"number_member\":123}");
    TEST_OBJECT(1, "{\"string_member\":\"hello world\"}");
    TEST_OBJECT(1, "{\"array_member\":[1, 2, 3]}");
    TEST_OBJECT(1, "{\"object_member\":{\"test_key\":1}}");
}

static void test_parse_invalid_object() {
    TEST_ERROR(LEPT_PARSE_MISS_KEY, "{:123}");
    TEST_ERROR(LEPT_PARSE_MISS_COLON, "{\"key\"123}");
    TEST_ERROR(LEPT_PARSE_MISS_COMMA_RO_CURLY_BRACKET, "{\"key_1\":1 \"key_2\":2}");
    TEST_ERROR(LEPT_PARSE_MISS_COMMA_RO_CURLY_BRACKET, "{\"key\":1");
}
//===========================End: 数组==============================

//=========================== Start: 字符串化 ==============================
// 测试 literal 的字符串化
static void test_stringify_literal() {
    ROUNDTRIP("true");
    ROUNDTRIP("false");
    ROUNDTRIP("null");
}

// 测试 number 的字符串化
static void test_stringify_number() {
    ROUNDTRIP("0");
    ROUNDTRIP("123");
    ROUNDTRIP("10000000000");
    ROUNDTRIP("1e-10");
    ROUNDTRIP("1.0000000000000002");       // the smallest number > 1
    ROUNDTRIP("4.9406564584124654e-324");  // Min. subnormal positive double
    ROUNDTRIP("-4.9406564584124654e-324");
    ROUNDTRIP("2.2250738585072009e-308");  // Max. subnormal double
    ROUNDTRIP("-2.2250738585072009e-308");
    ROUNDTRIP("2.2250738585072014e-308");  // Min. normal positive double
    ROUNDTRIP("-2.2250738585072014e-308");
    ROUNDTRIP("1.7976931348623157e+308");  // Max. Double
    ROUNDTRIP("-1.7976931348623157e+308");
}

// 测试 string 的字符串化
static void test_stringify_string() {
    ROUNDTRIP("\"\\u0060\"");
    ROUNDTRIP("\"\\u052c\"");
    ROUNDTRIP("\"\\ua973\"");
    ROUNDTRIP("\"\\uda30deac\"");
    ROUNDTRIP("\"\\udbffdfff\"");
}

// 测试 array 的字符串化
static void test_stringify_array() {
    ROUNDTRIP("[1,2,3]");
    ROUNDTRIP("[1,[2,3]]");
    ROUNDTRIP("[1,[2,[3,[4,[5]]]]]");
    ROUNDTRIP("[[[[[[[[[1],2],3],4],5],6],7],8],9]");
    ROUNDTRIP("[true,false,null,123,\"\\u0061\\u0062\\u0063\",\"\\udbffdfff\"]");
}

// 测试 object 的字符串化
static void test_stringify_object() {
    ROUNDTRIP("{\"a\":1,\"b\":2}");
    ROUNDTRIP("{\"null_type\":null,\"boolean_type\":true,\"boolean_type\":false}");
    ROUNDTRIP("{\"number_type\":123}");
    ROUNDTRIP("{\"string_type\":\"\\u0068\\u0065\\u006c\\u006c\\u006f\\u0020\\u0077\\u006f\\u0072\\u006c\\u0064\"}");
    ROUNDTRIP("{\"array_type\":[1,2,3]}");
    ROUNDTRIP("{\"object_type\":{\"haha\":1}}");
}

// 字符串化测试集合
int match(__uint8_t byte, __uint8_t expect_prefix, __uint8_t expect_digit, __uint8_t* result);  // tmp declaration
static void test_stringify() {
    // test_stringify_literal();
    // test_stringify_number();
    // test_stringify_string();
    // test_stringify_array();
    test_stringify_object();
}
//=========================== End: 字符串化 ==============================

//解析测试
static void test_parse() {
#if 1
#    if 1
    test_parse_expect_literal();   //解析value字面量: true, false, null
    test_parse_invalid_literal();  //解析非法字面量
#    endif
#    if 1
    test_parse_expect_number();   //测试有效数字
    test_parse_invalid_number();  //测试非法数字
#    endif
#    if 1
#        if 1
    test_parse_expect_string();   //测试有效的不含 utf-8 字符的字符串
    test_parse_invalid_string();  //测试非法的不含 utf-8 字符的字符串
#        endif
#        if 1
    test_parse_expect_utf8_string();  //测试有效的 utf-8 字符串
    test_invalid_utf8_string();       //测试非法 utf-8 字符串
#        endif
#    endif
#    if 1
    test_parse_expect_array();
    test_parse_invalid_array();
#    endif
#    if 1
    test_parse_expect_object();
    test_parse_invalid_object();
#    endif
#    if 1
    test_stringify();
#    endif
#endif
}

int main(void) {
    test_parse();  //测试解析
    if (test_count == 0) {
        printf("No test.\n");
        return main_ret;
    }
    //打印通过次数、总测试次数、通过率
    printf("%d / %d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count);
    return main_ret;
}